package cn.itcast.nems.journal.scheduler;

import cn.hutool.core.lang.Assert;
import cn.itcast.nems.base.api.ClazzApi;
import cn.itcast.nems.base.dto.ClazzDTO;
import cn.itcast.nems.journal.dto.*;
import cn.itcast.nems.journal.enumeration.JournalTransferLineStateEnum;
import cn.itcast.nems.journal.service.JournalTransferDelayService;
import cn.itcast.nems.journal.service.JournalTransferReasonServiceV2;
import cn.itcast.nems.journal.service.JournalTransferService;
import cn.itcast.nems.journal.service.JournalTransferSuspendedService;
import cn.itcast.nems.journal.util.TransferUtils;
import cn.itcast.nems.order.dto.PaymentReceiptStateEnum;
import cn.itcast.nems.studentrecord.bo.EditSuspendJournalParams;
import cn.itcast.nems.studentrecord.dao.entity.JournalTransfer;
import cn.itcast.nems.studentrecord.dao.entity.JournalTransferBatchImport;
import cn.itcast.nems.studentrecord.dao.entity.JournalTransferType;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecord;
import cn.itcast.nems.studentrecord.dto.SuspendJournalVo;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordStateEnum;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordService;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Objects;

/**
 * 功能描述:添加转班（同学科、其他转班）、转待定、待定学员
 *
 * @author 陈早庆
 * @program: nems-server
 * @update:[变更日期2024-03-15][陈早庆][变更描述]
 * @since 2024-03-15
 */
@Component
@RequiredArgsConstructor
public class BatchImportJournalTransferHandler {
    private final ClazzApi clazzApi;
    private final StudentRecordService studentRecordService;
    private final JournalTransferReasonServiceV2 reasonService;
    private final JournalTransferService journalTransferService;
    private final JournalTransferDelayService journalTransferDelayService;
    private final JournalTransferSuspendedService journalTransferSuspendedService;
    private final JournalTransferBatchImportService journalTransferBatchImportService;
    
    /**
     * 添加转班（同学科、其他转班）、转待定、待定学员
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void importTransfer(JournalTransferBatchImport journal, JournalTransferType journalType) {
        // 转入班级
        ClazzDTO clazzDTO = getTargetClazz(journal.getTargetClazzId());
        
        JournalTransferReasonVDTO reason = getReason(journalType, journal.getBizReason());
        
        JournalTransferCreateBo journalTransferCreateBo = new JournalTransferCreateBo();
        journalTransferCreateBo.setType(journalType);
        journalTransferCreateBo.setReasonCode(reason.getCode());
        journalTransferCreateBo.setTargetClazzCampusId(clazzDTO.getSchoolId());
        journalTransferCreateBo.setTargetClazzModelId(clazzDTO.getModelId());
        journalTransferCreateBo.setTargetClazzSubjectId(clazzDTO.getSubjectId());
        journalTransferCreateBo.setTargetClazzId(clazzDTO.getClassId());
        journalTransferCreateBo.setSpecialFlag(journal.getSpecialFlag().equals("是"));
        journalTransferCreateBo.setRemark(journal.getRemark());
        journalTransferCreateBo.setDirection(convertOperateTypeToDirection(journal.getOperatorType()));
        
        // 添加转班头
        String journalTransferId = journalTransferService.create(journalTransferCreateBo);
        Assert.notBlank(journalTransferId, "创建凭单头失败！");
        
        // 添加转班凭单-行
        importTransferLine(journalType, journal, journalTransferId, reason.getCode(), journal.getOperatorType());
        
        // 保存任务结果
        journalTransferBatchImportService.update(journal.getId(), JournalBatchImportProcessState.FINISHED.name(), "导入凭单成功！", getProcessRetriesNumber(journal), journalTransferId);
    }

    /**
     * 添加转班凭单-行
     *
     * @param type              凭单类型
     * @param journal           凭单数据
     * @param journalTransferId 凭单头id
     * @param reasonCode        原因code
     * @param direction         说明
     * @return
     */
    private boolean importTransferLine(final JournalTransferType type, final JournalTransferBatchImport journal,
            final String journalTransferId, final String reasonCode, final String direction) {
        
        JournalTransfer journalTransferInfoVDto = journalTransferService.getById(journalTransferId);
        Assert.notNull(journalTransferInfoVDto, "获取凭单头失败！");
        
        String reasonDescription = journal.getBizReason();
        JournalTransferInfoBDto journalTransferInfoBDto = new JournalTransferInfoBDto();
        journalTransferInfoBDto.setId(journalTransferId);
        journalTransferInfoBDto.setTargetClazzId(journalTransferInfoVDto.getTargetClazzId());
        journalTransferInfoBDto.setTargetClazzSubjectId(journalTransferInfoVDto.getTargetClazzSubjectId());
        journalTransferInfoBDto.setTargetClazzCampusId(journalTransferInfoVDto.getTargetClazzCampusId());
        journalTransferInfoBDto.setTargetClazzModelId(journalTransferInfoVDto.getTargetClazzModelId());
        journalTransferInfoBDto.setType(type);
        journalTransferInfoBDto.setSpecialFlag(journal.getSpecialFlag().equals("是"));
        journalTransferInfoBDto.setRemark(journal.getRemark());
        journalTransferInfoBDto.setState(PaymentReceiptStateEnum.PENDING.name());
        journalTransferInfoBDto.setReasonCode(reasonCode);
        journalTransferInfoBDto.setReasonDescription(reasonDescription);
        journalTransferInfoBDto.setDirection(direction);
        JournalTransferInfoBDtoLine journalTransferLine = new JournalTransferInfoBDtoLine();
        String originStudentRecordId = journal.getOriginStudentRecordId();
        // 获取转出班学籍
        StudentRecord originsStudentRecord = getOriginsStudentRecord(originStudentRecordId);
        
        journalTransferLine.setOriginStudentRecordId(originStudentRecordId);
        journalTransferLine.setOriginClazzId(originsStudentRecord.getClazzId());
        journalTransferLine.setOriginClazzName(originsStudentRecord.getClazzName());
        journalTransferLine.setOriginClazzInDate(originsStudentRecord.getInDate().toLocalDate());
        journalTransferLine.setOriginClazzOutDate(journal.getOriginClazzOutDate().toLocalDate());
        journalTransferLine.setOriginClazzOriginalAmountFormatted(TransferUtils.formatPrice(originsStudentRecord.getPrice()));
        journalTransferLine.setOriginClazzDiscountAmountFormatted(TransferUtils.formatPrice(originsStudentRecord.getDiscountAmount()));
        journalTransferLine.setOriginClazzAmountDueFormatted(TransferUtils.formatPrice(originsStudentRecord.getAmountDue()));
        journalTransferLine.setOriginActualAmountDueFormatted(TransferUtils.formatPrice(originsStudentRecord.getActualAmountDue()));
        journalTransferLine.setOriginClazzSettlementAmountFormatted(TransferUtils.formatPrice(originsStudentRecord.getSettlementAmount()));
        journalTransferLine.setOriginClazzActualSettlementAmountFormatted(TransferUtils.formatPrice(originsStudentRecord.getTransferOutAmount()));
        journalTransferLine.setOriginClazzServiceDays(journal.getOriginClazzServiceDays());
        journalTransferLine.setOriginClazzSettlementDays(journal.getOriginClazzSettlementDays());
        journalTransferLine.setOriginClazzActualSettlementDays(journal.getOriginClazzSettlementDays());
        
        journalTransferLine.setTargetClazzInDate(LocalDate.from(journal.getTargetClazzInDate()));
        journalTransferLine.setTargetClazzOriginalAmountFormatted(TransferUtils.formatPrice(journal.getTargetAmountDue()));
        journalTransferLine.setTargetClazzDiscountAmountFormatted(TransferUtils.formatPrice(journal.getTargetDiscountAmount()));
        journalTransferLine.setTargetClazzAmountDueFormatted(TransferUtils.formatPrice(journal.getTargetAmountDue()));
        journalTransferLine.setTargetClazzActualAmountDueFormatted(TransferUtils.formatPrice(journal.getTargetAmountDue()));
        journalTransferLine.setTargetClazzTransferAmountFormatted(TransferUtils.formatPrice(journal.getTargetTransferFromAmount()));
        
        journalTransferLine.setState(JournalTransferLineStateEnum.PENDING.name());
        journalTransferInfoBDto.setLineList(Arrays.asList(journalTransferLine));
        return journalTransferService.update(journalTransferInfoBDto);
    }

    /**
     * 待定班学员（转确定）
     *
     * @param direction
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void importDelay(JournalTransferBatchImport journal) {
        String originStudentRecordId = journal.getOriginStudentRecordId();
        // 获取转出班学籍
        getOriginsStudentRecord(originStudentRecordId);
        
        // 转入班级
        ClazzDTO clazzDTO = getTargetClazz(journal.getTargetClazzId());
        
        JournalTransferDelayCreateBo createBo = new JournalTransferDelayCreateBo();
        createBo.setOriginStudentRecordId(originStudentRecordId);
        createBo.setOriginClazzOutDate(journal.getOriginClazzOutDate().toLocalDate());
        createBo.setTargetClazzId(clazzDTO.getClassId());
        createBo.setTargetClazzInDate(journal.getTargetClazzInDate().toLocalDate());
        createBo.setDirection(convertOperateTypeToDirection(journal.getOperatorType()));
        createBo.setState(JournalTransferLineStateEnum.PENDING.name());
        createBo.setRemark(journal.getRemark());
        String journalID = journalTransferDelayService.createJournalTransferDelay(createBo);
        
        journalTransferBatchImportService.update(journal.getId(), JournalBatchImportProcessState.FINISHED.name(), "导入凭单成功！", getProcessRetriesNumber(journal), journalID);
    }


    /**
     * 休学
     *
     * @param direction
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void importSuspended(JournalTransferBatchImport journal) {
        // 转出学籍
        String originStudentRecordId = journal.getOriginStudentRecordId().trim();
        getOriginsStudentRecord(originStudentRecordId);
        
        // 休学班级
        ClazzDTO clazzDTO = getTargetClazz(journal.getTargetClazzId());
        
        String reasonCode = null;
        if (journal.getOperatorType().equals("休学")) {
            // 只有“休学”需要原因，“休学归来”不需要原因
            reasonCode= getReason(JournalTransferType.SUSPENSION, journal.getBizReason()).getCode();
        }
        
        EditSuspendJournalParams params = new EditSuspendJournalParams();
        params.setOriginStudentRecordId(originStudentRecordId);
        params.setOriginClazzOutDate(journal.getOriginClazzOutDate().toLocalDate());
        params.setReasonCode(reasonCode);
        params.setTargetClazzInDate(journal.getTargetClazzInDate().toLocalDate());
        params.setSpecialFlag(journal.getSpecialFlag().equals("是"));
        params.setDirection(convertOperateTypeToDirection(journal.getOperatorType()));
        params.setRemark(journal.getRemark());
        params.setState(PaymentReceiptStateEnum.PENDING.name());
        params.setTargetClazzId(clazzDTO.getClassId());
        SuspendJournalVo rst = journalTransferSuspendedService.editSuspendJournal(params);
        String journalId = Objects.isNull(rst) ? "" : rst.getId();
        
        journalTransferBatchImportService.update(journal.getId(), JournalBatchImportProcessState.FINISHED.name(), "导入凭单成功！", getProcessRetriesNumber(journal), journalId);
    }
    
    /**
     * 
     * 功能描述：将“操作类型”转换为系统功能所需的操作类型（用于区分休学、休学归来、转待定、转确定）
     *
     * @param operateType 导入任务的操作类型
     * @return 系统功能所需要的操作类型
     * 
     * @author 刘曙
     *
     * @since 2024年3月17日
     *
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     */
    private String convertOperateTypeToDirection(@NonNull String operateType) {
        operateType = operateType.trim();
        return switch (operateType) {
            case "同学科转班" -> "同学科转班";
            case "其他转班" -> "其他转班";
            case "待定班" -> "转入待定班";
            case "待定班学员（转确定）" -> "转入确定班";
            case "休学" -> "休学";
            case "休学学员（休学归来）" -> "休学归来";
            default -> throw new IllegalArgumentException("未知操作类型: " + operateType);
        };
    }
    
    /**
     * 
     * 功能描述：获取系统功能上指定的凭单办理业务原因
     *
     * @param journalType 凭单类型
     * @param bizReason 导入任务填写的业务原因
     * @return 业务原因对象
     * @throws 业务原因不存在时，抛出IllegalArgumentException异常
     * 
     * @author 刘曙
     *
     * @since 2024年3月17日
     *
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     */
    private JournalTransferReasonVDTO getReason(JournalTransferType journalType, @NonNull String bizReason) {
        JournalTransferReasonVDTO reason = reasonService.allByName().getByTypeAndName(journalType, bizReason);
        Assert.notNull(reason, "业务原因不存在！", bizReason);
        return reason;
    }
    
    /**
     * 
     * 功能描述：获取任务执行结束以后的任务执行次数
     *
     * @param task 当前任务
     * @return 任务结束以后的执行次数
     * 
     * @author 刘曙
     *
     * @since 2024年3月17日
     *
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     */
    private int getProcessRetriesNumber(JournalTransferBatchImport task) {
        int currentNumber = Objects.isNull(task.getProcessRetriesNumber()) ? 0 : task.getProcessRetriesNumber(); 
        return currentNumber + 1;
    }
    
    /**
     * 
     * 功能描述：获取班级对象
     *
     * @param targetClazzId 转入班级ID
     * @return 班级对象
     * 
     * @author 刘曙
     *
     * @since 2024年3月19日
     *
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     */
    public ClazzDTO getTargetClazz(String targetClazzId) {
        Integer clazzId = 0;
        try {
            clazzId = Integer.valueOf(targetClazzId.trim());
        } catch (Exception e) {
            throw new IllegalArgumentException("转入班级ID格式不正确！");
        }
        
        ClazzDTO clazzDTO = clazzApi.queryByClassId(clazzId);
        Assert.notNull(clazzDTO, "转入班级ID不存在或不正确！");
        return clazzDTO;
    }
    
    /**
     * 
     * 功能描述：获取转出学籍信息
     *
     * @param originStudentRecordId 转出学籍ID
     * @return 转出学籍信息
     * 
     * @author 刘曙
     *
     * @since 2024年3月20日
     *
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     */
    public StudentRecord getOriginsStudentRecord(String originStudentRecordId) {
        StudentRecord originsStudentRecord = studentRecordService.getById(originStudentRecordId.trim());
        Assert.notNull(originsStudentRecord, "转出班学籍不存在，请检查学籍ID是否正确！");
        Assert.isTrue(originsStudentRecord.getState().equalsIgnoreCase(StudentRecordStateEnum.VALID.name()), "转出班学籍尚未生效，不能办理转班！");
        return originsStudentRecord;
    }
}
